#pragma once
#include <ostream>

#include "detail/basic_formatter.hpp"
#include "inc_windows_h.hpp"
#include "wxx_error.hpp"
#include "string.hpp"

#define WXX_os_info_type(name, flag)   bool name() const throw() { return test_type(flag); }
#define WXX_os_info_suite(name, flag)  bool name() const throw() { return test_suite(flag); }
#define WXX_os_info_metric(name, flag) bool name() const throw() { return ::GetSystemMetrics(flag) != 0; }

#ifndef VER_SUITE_WH_SERVER
#define VER_SUITE_WH_SERVER 0x00008000
#endif

namespace wxx
{
	struct os_info
	{
		explicit os_info() throw(...) 
			: m_info(get_os_info()) 
		{
		}

		unsigned int version_major() const throw()
		{
			return m_info.dwMajorVersion;
		}

		unsigned int version_minor() const throw()
		{
			return m_info.dwMinorVersion;
		}

		unsigned int version_build() const throw()
		{
			return m_info.dwBuildNumber;
		}

		unsigned int sp_major() const throw()
		{
			return m_info.wServicePackMajor;
		}

		unsigned int sp_minor() const throw()
		{
			return m_info.wServicePackMinor;
		}

		tstring sp_description() const
		{
			return tstring(m_info.szCSDVersion);
		}

		tstring description() const
		{
			tostringstream stream;
			stream << TEXT("Microsoft Windows ");

			if (version_major() == 6)
			{
				if (is_client()) { stream << TEXT("Vista "); }
				else             { stream << TEXT("Server 2008 "); }
			}
			else if (version_major() == 5 && version_minor() == 2)
			{
				// Type

				if (is_server_r2()) 
					stream << TEXT("Server 2003 R2, ");
				
				else if (is_server_storage())
					stream << TEXT("Storage Server 2003");

				else if (is_server_compute())
					stream << TEXT("Compute Cluster");

				else if (is_client())
					stream << TEXT("XP Professional x64 Edition");
				
				// Edition

				if (!is_client())
				{

				}



			}

			return stream.str();
		}


		bool is_server() const { return !is_client(); }
		bool is_64bit() const
		{
		}
		
		WXX_os_info_type  (is_dc,                   VER_NT_DOMAIN_CONTROLLER)
		WXX_os_info_type  (is_client,               VER_NT_WORKSTATION)
		WXX_os_info_suite (is_client_home,          VER_SUITE_PERSONAL)

		WXX_os_info_metric(is_client_mediacenter,   SM_MEDIACENTER)
		WXX_os_info_metric(is_client_starter,       SM_STARTER)
		WXX_os_info_metric(is_client_tabletpc,      SM_TABLETPC)

		WXX_os_info_suite (is_server_web,           VER_SUITE_BLADE)
		WXX_os_info_suite (is_server_compute,       VER_SUITE_COMPUTE_SERVER)
		WXX_os_info_suite (is_server_enterprise,    VER_SUITE_ENTERPRISE)
		WXX_os_info_suite (is_server_datacenter,    VER_SUITE_DATACENTER)
		WXX_os_info_suite (is_server_storage,       VER_SUITE_STORAGE_SERVER)
		WXX_os_info_suite (is_server_home,          VER_SUITE_WH_SERVER)
		WXX_os_info_metric(is_server_r2,            SM_SERVERR2)


	private:

		OSVERSIONINFOEX m_info;

		bool test_suite(unsigned short flag) const throw()
		{
			return 0 != (m_info.wSuiteMask & flag);
		}

		bool test_type(unsigned short flag) const throw()
		{
			return 0 != (m_info.wProductType & flag);
		}

		static OSVERSIONINFOEX get_os_info()
		{
			OSVERSIONINFOEX info = { sizeof(OSVERSIONINFOEX) };
			
			ensure_success_win32(
				::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info))
			);

			

			return info;
		}
	};

	namespace detail
	{
		template<class Ch, class Tr>
		struct os_info_formatter : basic_formatter<os_info, Ch, Tr>
		{
			explicit os_info_formatter(const os_info& info)
				: basic_formatter(info)
			{
			}
			
			ostream_type& write_to(ostream_type& output)
			{
				return output << m_value.description();
			}
		};
	}

	template <class _Elem, class _Traits>
	std::basic_ostream<_Elem, _Traits>& 
	operator<<(std::basic_ostream<_Elem, _Traits>& output, const os_info& info)
	{
		return detail::os_info_formatter<_Elem, _Traits>(info).write_to(output);
	}

}

#undef WXX_os_info_type
#undef WXX_os_info_suite
#undef WXX_os_info_metric